package com.jyw.flowable.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.jyw.common.core.page.TableDataInfo;
import com.jyw.flowable.domain.dto.*;
import com.jyw.flowable.domain.vo.WfModelVo;
import com.jyw.flowable.service.IWfModelService;
import com.jyw.flowable.utils.ModelUtil;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Created by ZhangCheng on 2023/11/13
 */
@Service
public class WfModelServiceImpl implements IWfModelService {

    @Resource
    private RepositoryService repositoryService;

    /**
     * 文件后缀
     */
    public static final String SUFFIX = ".bpmn";

    @Override
    public TableDataInfo<WfModelVo> list(WfModelPageDto wfModelPageDto) {
        ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByCreateTime().desc();
        // 执行查询
        long pageTotal = modelQuery.count();
        if (pageTotal <= 0) {
            return TableDataInfo.build();
        }
        // 构建查询条件
        if (StrUtil.isNotBlank(wfModelPageDto.getModelKey())) {
            modelQuery.modelKey(wfModelPageDto.getModelKey());
        }
        if (StrUtil.isNotBlank(wfModelPageDto.getModelName())) {
            modelQuery.modelNameLike("%" + wfModelPageDto.getModelName() + "%");
        }
        if (StrUtil.isNotBlank(wfModelPageDto.getCategory())) {
            modelQuery.modelCategory(wfModelPageDto.getCategory());
        }
        int offset = wfModelPageDto.getPageSize() * (wfModelPageDto.getPageNum() - 1);
        List<Model> modelList = modelQuery.listPage(offset, wfModelPageDto.getPageSize());
        List<WfModelVo> modelVoList = new ArrayList<>(modelList.size());
        modelList.forEach(model -> {
            WfModelVo modelVo = new WfModelVo();
            modelVo.setModelId(model.getId());
            modelVo.setModelName(model.getName());
            modelVo.setModelKey(model.getKey());
            modelVo.setCategory(model.getCategory());

            if (model.getMetaInfo() != null) {
                WfMetaInfoDto infoDto = JSON.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
                if (infoDto != null) {
                    modelVo.setCreateTime(infoDto.getCreateTime());
                    modelVo.setRemark(infoDto.getRemark());
                }
            }
            modelVo.setVersion(model.getVersion());
            modelVoList.add(modelVo);
        });
        return TableDataInfo.build(modelVoList, pageTotal);
    }

    @Override
    public String queryBpmnXmlById(String modelId) {
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        return StrUtil.utf8Str(bpmnBytes);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(WfModelAddDto wfModelAddDto) {
        Model model = repositoryService.newModel();
        model.setName(wfModelAddDto.getModelName());
        model.setKey(wfModelAddDto.getModelKey());
        model.setCategory(wfModelAddDto.getCategory());
        WfMetaInfoDto infoDto = WfMetaInfoDto.builder()
                .remark(wfModelAddDto.getRemark())
                .createTime(LocalDateTime.now())
                .build();
        String metaInfo = JSON.toJSONString(infoDto);
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
    }

    @Override
    public void edit(WfModelEditDto wfModelEditDto) {
        // 根据模型Key查询模型信息
        Model model = repositoryService.getModel(wfModelEditDto.getModelId());
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        model.setCategory(wfModelEditDto.getCategory());
        WfMetaInfoDto infoDto = JSON.parseObject(model.getMetaInfo(), WfMetaInfoDto.class);
        infoDto.setRemark(wfModelEditDto.getRemark());
        String metaInfo = JSON.toJSONString(infoDto);
        model.setMetaInfo(metaInfo);
        // 保存流程模型
        repositoryService.saveModel(model);
    }

    @Override
    public void save(WfModelSaveDto wfModelSaveDto) {
        // 查询模型信息
        Model model = repositoryService.getModel(wfModelSaveDto.getModelId());
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        BpmnModel bpmnModel = ModelUtil.getBpmnModel(wfModelSaveDto.getBpmnXml());
        if (ObjectUtil.isEmpty(bpmnModel)) {
            throw new RuntimeException("获取模型设计失败！");
        }
        String processName = bpmnModel.getMainProcess().getName();
        // 获取开始节点
        StartEvent startEvent = ModelUtil.getStartEvent(bpmnModel);
        if (ObjectUtil.isNull(startEvent)) {
            throw new RuntimeException("开始节点不存在，请检查流程设计是否有误！");
        }
        // 获取开始节点配置的表单Key
        /*if (StrUtil.isBlank(startEvent.getFormKey())) {
            throw new RuntimeException("请配置流程表单");
        }*/
        Model newModel;
        if (Boolean.TRUE.equals(wfModelSaveDto.getNewVersion())) {
            newModel = repositoryService.newModel();
            newModel.setName(processName);
            newModel.setKey(model.getKey());
            newModel.setCategory(model.getCategory());
            newModel.setMetaInfo(model.getMetaInfo());
            newModel.setVersion(model.getVersion() + 1);
        } else {
            newModel = model;
            // 设置流程名称
            newModel.setName(processName);
        }
        // 保存流程模型
        repositoryService.saveModel(newModel);
        // 保存 BPMN XML
        byte[] bpmnXmlBytes = StringUtils.getBytes(wfModelSaveDto.getBpmnXml(), StandardCharsets.UTF_8);
        repositoryService.addModelEditorSource(newModel.getId(), bpmnXmlBytes);
    }

    @Override
    public void deployModel(String modelId) {
        // 获取流程模型
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            throw new RuntimeException("流程模型不存在！");
        }
        // 获取流程图
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        if (ArrayUtil.isEmpty(bpmnBytes)) {
            throw new RuntimeException("请先设计流程图！");
        }
        String processName = model.getName() + SUFFIX;
        // 部署流程
        Deployment deployment = repositoryService.createDeployment()
                .name(model.getName())
                .key(model.getKey())
                .category(model.getCategory())
                .addBytes(processName, bpmnBytes)
                .deploy();
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        // 修改流程定义的分类，便于搜索流程
        repositoryService.setProcessDefinitionCategory(procDef.getId(), model.getCategory());
    }

    @Override
    public void deleteByIds(String[] ids) {
        Arrays.asList(ids).forEach(id -> {
            Model model = repositoryService.getModel(id);
            if (ObjectUtil.isNull(model)) {
                throw new RuntimeException("流程模型不存在！");
            }
            repositoryService.deleteModel(id);
        });
    }
}
